﻿// Inner Fire 游戏引擎库
// DxShadows 渲染器光源功能
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 用于DirectX的渲染器。
// 此文件分装用于光源功能
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-04-23

#include "dx_shadows.h"
#include "dx_graphics.h"

namespace ifire::dx {
ifire::dx::DxShadows::DxShadows(DxGraphics* dx) : DxModule(dx) {}

void DxShadows::UpdateShadowPass() {
  auto& light = dx_->GetLights();

  // 注意这里的矩阵，不再是相机的矩阵，而是阴影对应项目的矩阵
  XMMATRIX view = XMLoadFloat4x4(&light_view_);
  XMMATRIX proj = XMLoadFloat4x4(&light_proj_);

  XMMATRIX viewProj = XMMatrixMultiply(view, proj);
  auto det = XMMatrixDeterminant(view);
  XMMATRIX invView = XMMatrixInverse(&det, view);
  det = XMMatrixDeterminant(proj);
  XMMATRIX invProj = XMMatrixInverse(&det, proj);
  det = XMMatrixDeterminant(viewProj);
  XMMATRIX invViewProj = XMMatrixInverse(&det, viewProj);

  UINT w = shadow_map_->Width();
  UINT h = shadow_map_->Height();

  XMStoreFloat4x4(&shadow_pass_.View, XMMatrixTranspose(view));
  XMStoreFloat4x4(&shadow_pass_.InvView, XMMatrixTranspose(invView));
  XMStoreFloat4x4(&shadow_pass_.Proj, XMMatrixTranspose(proj));
  XMStoreFloat4x4(&shadow_pass_.InvProj, XMMatrixTranspose(invProj));
  XMStoreFloat4x4(&shadow_pass_.ViewProj, XMMatrixTranspose(viewProj));
  XMStoreFloat4x4(&shadow_pass_.InvViewProj, XMMatrixTranspose(invViewProj));
  shadow_pass_.EyePosW = light_pos_w_;
  shadow_pass_.RenderTargetSize = XMFLOAT2((float)w, (float)h);
  shadow_pass_.InvRenderTargetSize = XMFLOAT2(1.0f / w, 1.0f / h);
  shadow_pass_.NearZ = light_near_z_;
  shadow_pass_.FarZ = light_far_z_;

  auto currPassCB = dx_->CurrentFrameRes()->pass_cb.get();
  currPassCB->CopyData(dx_->CurrentFrameRes()->ShadowPassIndex(), shadow_pass_);
}

void DxShadows::InitConfig() {
  auto& cfg = dx_->Config();
  // 这是阴影贴图里面使用的。目前没有计算场景大小的功能。因此手动配置大小。
  scene_bounds_.Center = XMFLOAT3(0.0f, 0.0f, 0.0f);
  scene_bounds_.Radius = cfg("SceneRadius");
}

void DxShadows::Init() {  
  shadow_map_ = std::make_unique<ShadowMap>(dx_->Device(), 2048, 2048);
}

void DxShadows::UpdateShadowTransform() {

  // 只计算第一个平行光的阴影！
  XMVECTOR lightDir = XMLoadFloat3(&dx_->GetMainPass().lights[0].direction);

  // 这里要注意了，平行光是只有方向，没有位置的。
  // 但是，必须要给平行光一个位置，才能覆盖要生成阴影的区域。
  // 目前只通过配置文件来配置一个估算的区域
  XMVECTOR lightPos = -2.0f * scene_bounds_.Radius * lightDir;
  XMFLOAT3 light_pos_value;
  XMStoreFloat3(&light_pos_value, lightPos);
  XMStoreFloat3(&light_pos_w_, lightPos);

  // 创建一个看向中心点（目前是世界原点）的矢量
  XMVECTOR targetPos = XMLoadFloat3(&scene_bounds_.Center);
  XMVECTOR lightUp = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
  XMMATRIX lightView = XMMatrixLookAtLH(lightPos, targetPos, lightUp);

  // 将包围球体变换到光源空间。
  XMFLOAT3 sphereCenterLS;
  XMStoreFloat3(&sphereCenterLS, XMVector3TransformCoord(targetPos, lightView));

  // 在光源空间中的正交截锥体包围了场景。
  float l = sphereCenterLS.x - scene_bounds_.Radius;
  float b = sphereCenterLS.y - scene_bounds_.Radius;
  float n = sphereCenterLS.z - scene_bounds_.Radius;
  float r = sphereCenterLS.x + scene_bounds_.Radius;
  float t = sphereCenterLS.y + scene_bounds_.Radius;
  float f = sphereCenterLS.z + scene_bounds_.Radius;

  light_near_z_ = n;
  light_far_z_ = f;
  XMMATRIX lightProj = XMMatrixOrthographicOffCenterLH(l, r, b, t, n, f);

  // Transform NDC space [-1,+1]^2 to texture space [0,1]^2
  XMMATRIX T(0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
      0.0f, 0.5f, 0.5f, 0.0f, 1.0f);

  XMMATRIX S = lightView * lightProj * T;
  XMStoreFloat4x4(&light_view_, lightView);
  XMStoreFloat4x4(&light_proj_, lightProj);
  XMStoreFloat4x4(&shadow_transform_, S);
}

} // namespace ifire::dx